﻿using System;
using System.Collections.Generic;

namespace TShooter.TfsSearch.ProtocolHandler.Core
{
    public class TfsUrl
    {
        public const string SCHEME_HTTP = "tfs";
        public const string SCHEME_HTTPS = "tfss";
        private const int DEFAULT_PORT = 8080;
        private const string PARAM_METHOD = "m";

        private readonly Uri _RawUrl;
        private readonly string _ServerName;
        private readonly int _ServerPort;
        private readonly string _TeamProject;
        private readonly string _UserName;
        private readonly string _Password;
        private readonly TfsStore _Store;
        private readonly int _ID;
        private readonly bool _UseSSL;
        readonly Dictionary<string,string> _Parameters = new Dictionary<string, string>();
        private readonly DisplayMethod _Method;

        public TfsUrl(Uri url)
        {
            ValidateArguments(url);

            try
            {
                _ServerName = url.Host;
                _TeamProject = url.Segments[1].TrimEnd('/');
                _ServerPort = (url.Port == -1 ? DEFAULT_PORT : url.Port);
                _Store = (TfsStore)Enum.Parse(typeof(TfsStore), url.Segments[2].TrimEnd('/'), true);
                _ID = (url.Segments.Length == 4 ? int.Parse(url.Segments[3].TrimEnd('/')) : -1);
                _UseSSL = (url.Scheme.Equals(SCHEME_HTTPS, StringComparison.OrdinalIgnoreCase));
                _RawUrl = url;

                if (!string.IsNullOrEmpty(url.UserInfo))
                {
                    ParseUserInfo(url.UserInfo, out _UserName, out _Password);
                }

                if (!string.IsNullOrEmpty(url.Query) && url.Query.Length > 1)
                {
                    ParseQueryString(url.Query);
                }

                if (_Parameters.ContainsKey(PARAM_METHOD))
                {
                    ParseMethod(_Parameters[PARAM_METHOD], out _Method);
                }
            }
            catch (Exception ex)
            {
                throw new UriFormatException("Error parsing URL: " + ex.Message);
            }
        }

        private static void ValidateArguments(Uri url)
        {
            if (url == null)
            {
                throw new ArgumentNullException("url");
            }

            string scheme = url.Scheme;

            if (!scheme.Equals(SCHEME_HTTP, StringComparison.OrdinalIgnoreCase) && 
                !scheme.Equals(SCHEME_HTTPS, StringComparison.OrdinalIgnoreCase))
            {
                throw new UriFormatException(string.Format("Invalid scheme: {0}", scheme));
            }

            if (url.Segments.Length < 3 || url.Segments.Length > 4)
            {
                throw new UriFormatException("Invalid URL. Expected format:\n\ntfs[s]://<server>[:<port>]/<teamproject>/<store>[/<id>][?<parameters>]");
            }
        }

        private static void ParseMethod(string value, out DisplayMethod method)
        {
            method = (DisplayMethod) Enum.Parse(typeof (DisplayMethod), value, true);
        }

        private static void ParseUserInfo(string userInfo, out string userName, out string password)
        {
            int pos = userInfo.IndexOf(':');

            if (pos == -1)
            {
                userName = userInfo;
                password = null;
            }
            else
            {
                userName = userInfo.Substring(0, pos);
                password = userInfo.Substring(pos + 1);
            }
        }

        private void ParseQueryString(string query)
        {
            string[] parameters = query.Substring(1).Split(';');

            foreach (string parameter in parameters)
            {
                int pos = parameter.IndexOf('=');

                if (pos == -1)
                {
                    _Parameters.Add(parameter, "");
                }
                else
                {
                    _Parameters.Add(parameter.Substring(0, pos), parameter.Substring(pos+1));
                }
            }
        }

        public TfsUrl(string url)
            :this(new Uri(url))
        {
        }

        public Uri RawUrl
        {
            get { return _RawUrl; }
        }

        public Uri ServerUrl
        {
            get
            {
                string scheme = (UseSSL ? "https" : "http");
                return new Uri(string.Format("{0}://{1}:{2}", scheme, _ServerName, _ServerPort));
            }
        }

        public string ServerName
        {
            get { return _ServerName; }
        }

        public int ServerPort
        {
            get { return _ServerPort; }
        }

        public string TeamProject
        {
            get { return _TeamProject; }
        }

        public TfsStore Store
        {
            get { return _Store; }
        }

        public bool UseSSL
        {
            get { return _UseSSL; }
        }

        public Dictionary<string, string> Parameters
        {
            get { return _Parameters; }
        }

        public int ID
        {
            get { return _ID; }
        }

        public string UserName
        {
            get { return _UserName; }
        }

        public string Password
        {
            get { return _Password; }
        }

        public DisplayMethod Method
        {
            get { return _Method; }
        }
    }
}
